#include "Mysql.hpp"

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql___construct, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_connect, 0, 0, 4)
	ZEND_ARG_INFO(0, host) // host or :/lalala/mysql.socket
	ZEND_ARG_INFO(0, user)
	ZEND_ARG_INFO(0, password)
	ZEND_ARG_INFO(0, db)
	ZEND_ARG_INFO(0, port)
	ZEND_ARG_INFO(0, persistent)
	ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_setCharset, 0, 0, 1)
	ZEND_ARG_INFO(0, charset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_escape, 0, 0, 1)
	ZEND_ARG_INFO(0, string)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_implode, 0, 0, 1)
	ZEND_ARG_INFO(0, args)
	ZEND_ARG_INFO(0, separator)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepare, 0, 0, 2)
	ZEND_ARG_INFO(0, query)
	ZEND_ARG_INFO(0, args)
	ZEND_ARG_INFO(0, offset)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_query, 0, 0, 1)
	ZEND_ARG_INFO(0, string)
ZEND_END_ARG_INFO()


ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_beginTransaction, 0, 0, 1)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_commit, 0, 0, 1)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_rollback, 0, 0, 1)
ZEND_END_ARG_INFO()


ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_transaction, 0, 0, 1)
	ZEND_ARG_INFO(0, callback)
	ZEND_ARG_INFO(0, on_success)
	ZEND_ARG_INFO(0, on_fail)
	ZEND_ARG_INFO(0, disable_exception)
	ZEND_ARG_INFO(0, args)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_inTransaction, 0, 0, 1)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_numRows, 0, 0, 0)
	ZEND_ARG_INFO(0, result)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_affectedRows, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_fetchArray, 0, 0, 0)
	ZEND_ARG_INFO(0, result)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_fetchRow, 0, 0, 0)
	ZEND_ARG_INFO(0, result)
	ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_fetchAssoc, 0, 0, 0)
	ZEND_ARG_INFO(0, result)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_info, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_getInfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_matchedRows, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_fetchAll, 0, 0, 0)
	ZEND_ARG_INFO(0, result)
	ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_result, 0, 0, 0)
	ZEND_ARG_INFO(0, result)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_free, 0, 0, 0)
	ZEND_ARG_INFO(0, result)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_insertId, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_ping, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_close, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_error, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_errno, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_setLoggingQueries, 0, 0, 0)
	ZEND_ARG_INFO(0, flag)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_getQueriesList, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_resetQueriesList, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_getInstance, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_getMacrosByType, 0, 0, 2)
	ZEND_ARG_INFO(0, type)
	ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_getTableFields, 0, 0, 0)
	ZEND_ARG_INFO(0, table)
	ZEND_ARG_INFO(0, db)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareIntValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareUnsignedIntValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareStringValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareBoolValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareFloatValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareQueryValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareTableNameValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(arginfo_mysql_prepareValue, 0, 0, 1)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

static zend_function_entry php_mysql_exception_methods[] = { PHP_FE_END }; 
static zend_function_entry php_mysql_methods[] = {
	PHP_ME(Mysql, __construct,       arginfo_mysql___construct,       ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
	PHP_ME(Mysql, connect,           arginfo_mysql_connect,           ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, setCharset,        arginfo_mysql_setCharset,        ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, escape,            arginfo_mysql_escape,            ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, implode,           arginfo_mysql_implode,           ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepare,           arginfo_mysql_prepare,           ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, query,             arginfo_mysql_query,             ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, beginTransaction,  arginfo_mysql_beginTransaction,  ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, commit,            arginfo_mysql_commit,            ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, rollback,          arginfo_mysql_rollback,          ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, transaction,       arginfo_mysql_transaction,       ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, inTransaction,     arginfo_mysql_inTransaction,     ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, numRows,           arginfo_mysql_numRows,           ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, fetchArray,        arginfo_mysql_fetchArray,        ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, fetchRow,          arginfo_mysql_fetchRow,          ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, fetchAssoc,        arginfo_mysql_fetchAssoc,        ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, fetchAll,          arginfo_mysql_fetchAll,          ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, affectedRows,      arginfo_mysql_affectedRows,      ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, info,              arginfo_mysql_info,              ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, getInfo,           arginfo_mysql_getInfo,           ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, result,            arginfo_mysql_result,            ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, free,              arginfo_mysql_free,              ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, insertId,          arginfo_mysql_insertId,          ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, ping,              arginfo_mysql_ping,              ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, close,             arginfo_mysql_close,             ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, error,             arginfo_mysql_error,             ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, errno,             arginfo_mysql_errno,             ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, setLoggingQueries, arginfo_mysql_setLoggingQueries, ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, getQueriesList,    arginfo_mysql_getQueriesList,    ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, resetQueriesList,  arginfo_mysql_resetQueriesList,  ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, getInstance,       arginfo_mysql_getInstance,       ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(Mysql, getMacrosByType,   arginfo_mysql_getMacrosByType,   ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
	PHP_ME(Mysql, getTableFields,    arginfo_mysql_getTableFields,    ZEND_ACC_PUBLIC)
	
	// Prepare functions
	PHP_ME(Mysql, prepareIntValue,          arginfo_mysql_prepareIntValue,         ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepareUnsignedIntValue,  arginfo_mysql_prepareUnsignedIntValue, ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepareStringValue,       arginfo_mysql_prepareStringValue,      ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepareBoolValue,         arginfo_mysql_prepareBoolValue,        ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepareFloatValue,        arginfo_mysql_prepareFloatValue,       ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepareQueryValue,        arginfo_mysql_prepareQueryValue,       ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepareTableNameValue,    arginfo_mysql_prepareTableNameValue,   ZEND_ACC_PUBLIC)
	PHP_ME(Mysql, prepareValue,             arginfo_mysql_prepareValue,            ZEND_ACC_PUBLIC)
	PHP_FE_END
}; 

void php_mysql_free_storage(void *object TSRMLS_DC) {
	php_mysql_object *obj = (php_mysql_object *)object; 
	if (obj -> connect && !obj -> connect -> is_persistent)
		obj -> connect = evilworlds_mysql_close(obj -> connect); 
	obj -> connect = NULL; 
	zend_hash_destroy(obj -> std.properties); 
	FREE_HASHTABLE(obj -> std.properties); 
	efree(obj); 
}

zend_object_value php_mysql_create_handler(zend_class_entry *type TSRMLS_DC) {
	zend_object_value retval; 
	
	php_mysql_object *obj = (php_mysql_object *)emalloc(sizeof(php_mysql_object)); 
	memset(obj, 0, sizeof(php_mysql_object)); 
	obj -> std.ce = type; 
	
	ALLOC_HASHTABLE(obj -> std.properties); 
	zend_hash_init(obj -> std.properties, 0, NULL, ZVAL_PTR_DTOR, 0); 
	
	object_properties_init(&(obj -> std), php_mysql_ce); 
	
	retval.handle   = zend_objects_store_put(obj, NULL, php_mysql_free_storage, NULL TSRMLS_CC); 
	retval.handlers = &php_mysql_object_handlers; 
	return retval; 
}

// Инициализация драйвера
static inline void init_evilworlds_mysql_driver() {
	zend_class_entry ce; 
	
	// Evilworlds.Mysql
	INIT_CLASS_ENTRY(ce, MYSQL_CLASS_NAME, php_mysql_methods); 
	php_mysql_ce = zend_register_internal_class(&ce TSRMLS_CC); 
	php_mysql_ce -> create_object = php_mysql_create_handler;
	memcpy(&php_mysql_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 
	php_mysql_object_handlers.clone_obj = NULL; 
	php_mysql_ce -> ce_flags |= ZEND_ACC_FINAL_CLASS; 
	zend_declare_property_null(php_mysql_ce, const_str_length("instance"), ZEND_ACC_STATIC | ZEND_ACC_PUBLIC TSRMLS_CC); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("FIELD_ATTR_UNSIGNED"), FIELD_ATTR_UNSIGNED); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("FIELD_ATTR_ZEROFILL"), FIELD_ATTR_ZEROFILL); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_ASSOC"), MYSQL_ASSOC); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_NUM"), MYSQL_NUM); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_BOTH"), MYSQL_BOTH); 
	
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_CONNECT_TIMEOUT"), MYSQL_OPT_CONNECT_TIMEOUT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_COMPRESS"), MYSQL_OPT_COMPRESS); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_NAMED_PIPE"), MYSQL_OPT_NAMED_PIPE); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_INIT_COMMAND"), MYSQL_INIT_COMMAND); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_READ_DEFAULT_FILE"), MYSQL_READ_DEFAULT_FILE); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_READ_DEFAULT_GROUP"), MYSQL_READ_DEFAULT_GROUP); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_SET_CHARSET_DIR"), MYSQL_SET_CHARSET_DIR); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_SET_CHARSET_NAME"), MYSQL_SET_CHARSET_NAME); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_LOCAL_INFILE"), MYSQL_OPT_LOCAL_INFILE); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_PROTOCOL"), MYSQL_OPT_PROTOCOL); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_SHARED_MEMORY_BASE_NAME"), MYSQL_SHARED_MEMORY_BASE_NAME); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_READ_TIMEOUT"), MYSQL_OPT_READ_TIMEOUT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_WRITE_TIMEOUT"), MYSQL_OPT_WRITE_TIMEOUT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_USE_RESULT"), MYSQL_OPT_USE_RESULT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_USE_REMOTE_CONNECTION"), MYSQL_OPT_USE_REMOTE_CONNECTION); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_USE_EMBEDDED_CONNECTION"), MYSQL_OPT_USE_EMBEDDED_CONNECTION); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_SET_CLIENT_IP"), MYSQL_SET_CLIENT_IP); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_SECURE_AUTH"), MYSQL_SECURE_AUTH); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_REPORT_DATA_TRUNCATION"), MYSQL_REPORT_DATA_TRUNCATION); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_RECONNECT"), MYSQL_OPT_RECONNECT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_VERIFY_SERVER_CERT"), MYSQL_OPT_SSL_VERIFY_SERVER_CERT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_PLUGIN_DIR"), MYSQL_PLUGIN_DIR); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_DEFAULT_AUTH"), MYSQL_DEFAULT_AUTH); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_BIND"), MYSQL_OPT_BIND); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_KEY"), MYSQL_OPT_SSL_KEY); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_CERT"), MYSQL_OPT_SSL_CERT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_CA"), MYSQL_OPT_SSL_CA); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_CAPATH"), MYSQL_OPT_SSL_CAPATH); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_CIPHER"), MYSQL_OPT_SSL_CIPHER); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_CRL"), MYSQL_OPT_SSL_CRL); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_SSL_CRLPATH"), MYSQL_OPT_SSL_CRLPATH); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_PROGRESS_CALLBACK"), MYSQL_PROGRESS_CALLBACK); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_NONBLOCK"), MYSQL_OPT_NONBLOCK); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY"), MYSQL_OPT_USE_THREAD_SPECIFIC_MEMORY); 
	
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("TRANS_START_WITH_CONSISTENT_SNAPSHOT"), TRANS_START_WITH_CONSISTENT_SNAPSHOT); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("TRANS_START_READ_WRITE"), TRANS_START_READ_WRITE); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("TRANS_START_READ_ONLY"), TRANS_START_READ_ONLY); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("TRANS_NO_EXCEPTIONS"), TRANS_NO_EXCEPTIONS); 
	zend_declare_class_constant_long(php_mysql_ce, const_str_length("TRANS_SINGLE"), TRANS_SINGLE); 
	
	// Evilworlds.Mysql.Exception
	INIT_CLASS_ENTRY(ce, MYSQL_EXCEPTION_CLASS_NAME, php_mysql_exception_methods); 
	php_mysql_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(TSRMLS_C), NULL TSRMLS_CC); 
	memcpy(&php_mysql_exception_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 
	php_mysql_exception_object_handlers.clone_obj = NULL; 
	php_mysql_exception_ce -> ce_flags |= ZEND_ACC_FINAL_CLASS; 
	
	le_mysql_result = zend_register_list_destructors_ex(free_evilworlds_mysql_result, NULL, "evilworlds mysql result", evilworlds_module_entry.module_number); 
	EM_SG(persistent_cnt) = 0; 
}

// Уничтожение драйвера
static inline void destroy_evilworlds_mysql_driver() {
	evilworlds_mysql_close_all_persistent(); 
}

// Закрыть все постоянные соединения
static inline void evilworlds_mysql_close_all_persistent() {
	for (auto x: EM_SG(persistent_list))
		x.second = evilworlds_mysql_close(x.second); 
	EM_SG(persistent_list).empty(); 
}

// Очистка mysql результата
static void free_evilworlds_mysql_result(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
	MYSQL_RES *mysql_result = (MYSQL_RES *)rsrc -> ptr; 
	mysql_free_result(mysql_result); 
}

// Экранирование спец. символов
inline static unsigned long evilworlds_mysql_escape_string(mysql_connect_meta *conn, char *to, const char *from, unsigned long length) {
	if (conn && conn -> mysql)
		return mysql_real_escape_string(conn -> mysql, to, from, length); 
	return mysql_escape_string(to, from, length); 
}

// Очистка мета данных подключения
static inline void free_mysql_connect_meta(mysql_connect_meta *meta) {
	if (meta -> mysql) {
		void (*handler) (int) = signal(SIGPIPE, SIG_IGN); 
		mysql_close(meta -> mysql); 
		signal(SIGPIPE, handler); 
	}
	
	meta -> mysql                    = NULL; 
	meta -> is_persistent            = false; 
	meta -> active_transaction_level = 0; 
}

// Закрыть подключение
static inline mysql_connect_meta *evilworlds_mysql_close(mysql_connect_meta *meta) {
	if (meta) {
		free_mysql_connect_meta(meta); 
		free(meta); 
	}
	return NULL; 
}

// Ошибка mysql
static inline zval *evilworlds_mysql_error(mysql_connect_meta *meta) {
	if (meta && meta -> mysql)
		return zend_throw_exception_ex(php_mysql_exception_ce, mysql_errno(meta -> mysql), (char *)"Mysql error[%d]: %s", mysql_errno(meta -> mysql), mysql_error(meta -> mysql)); 
	return zend_throw_exception_ex(php_mysql_exception_ce, 0, (char *)"Unknown error occured!"); 
}

bool do_evilworlds_mysql_query(php_mysql_object *obj, const char *real_query, int real_query_length, MYSQL_RES **result) {
	timeval start_time, end_time; 
	unsigned char tries = 0; 
	
	if (obj -> logging)
		gettimeofday(&start_time, NULL); 
next_try_exec_query:
	int res = mysql_real_query(obj -> connect -> mysql, real_query, real_query_length); 
	if (res != 0) {
		int err = mysql_errno(obj -> connect -> mysql); 
		// Если это deadlock
		if ((err == 1205 || err == 1213) && tries < MAX_DEADLOCK_TRIES) {
			usleep(10000); // Ждём 10 ms
			++tries; 
			goto next_try_exec_query; 
		}
		EXCEPTION_MYSQL_ERROR_VA(
			"Can't execute query! Query=\"%s\" (%d)", 
			real_query, real_query_length
		); 
		return false; 
	}
	
	if (result) {
		*result = mysql_store_result(obj -> connect -> mysql); 
		if (!*result && mysql_field_count(obj -> connect -> mysql) > 0) {
			EXCEPTION_MYSQL_ERROR_VA(
				"Can't store result! Query=\"%s\" (%d)", 
				real_query, real_query_length
			); 
			return false; 
		}
	}
	
	if (obj -> logging)
		gettimeofday(&end_time, NULL); 
	
	if (obj -> logging) {
		unsigned long start_time_msec = ((unsigned long long)start_time.tv_sec * 1000000) + start_time.tv_usec; 
		unsigned long end_time_msec = ((unsigned long long)end_time.tv_sec * 1000000) + end_time.tv_usec; 
		
		zval *new_entry; 
		MAKE_STD_ZVAL(new_entry); 
		array_init(new_entry); 
		
		add_assoc_stringl(new_entry, "query", (char *)real_query, real_query_length, 1); 
		add_assoc_long(new_entry, "time", end_time_msec - start_time_msec); 
		add_assoc_long(new_entry, "tries", tries); 
		add_next_index_zval(obj -> log_queries, new_entry); 
	}
	
	return true; 
}

bool start_transaction(php_mysql_object *obj, long flags) {
	// Если это первый уровень транзакции
	char *query = NULL; int query_len = 0; 
	if (obj -> connect -> active_transaction_level == 0) {
		query_len = spprintf(
			&query, 0, "START TRANSACTION %s %s %s", 
			(flags & TRANS_START_WITH_CONSISTENT_SNAPSHOT) ? "WITH CONSISTENT SNAPSHOT" : "", 
			(flags & TRANS_START_READ_WRITE) ? "READ WRITE" : "", 
			(flags & TRANS_START_READ_ONLY) ? "READ ONLY" : ""
		); 
	} else {
		// Если это вложенная транзаеция
		query_len = spprintf (
			&query, 0, "SAVEPOINT EVILTRANSLEVEL%d", 
			obj -> connect -> active_transaction_level
		); 
	}
	bool retval = do_evilworlds_mysql_query(obj, query, query_len, NULL); 
	if (query)
		efree(query); 
	if (retval)
		++obj -> connect -> active_transaction_level; 
	return retval; 
}

bool commit_transaction(php_mysql_object *obj) {
	if (!obj -> connect -> active_transaction_level)
		return false; 
	bool retval; 
	if (obj -> connect -> active_transaction_level > 1) {
		char *release_query = NULL; 
		int release_query_len = spprintf(
			&release_query, 0, "RELEASE SAVEPOINT EVILTRANSLEVEL%d", 
			obj -> connect -> active_transaction_level - 1
		);
		retval = do_evilworlds_mysql_query(obj, release_query, release_query_len, NULL); 
		if (release_query)
			efree(release_query); 
	} else
		retval = do_evilworlds_mysql_query(obj, const_str_length("COMMIT"), NULL); 
	if (retval) {
		if (obj -> connect -> active_transaction_level > 0)
			--obj -> connect -> active_transaction_level; 
	}
	return retval; 
}

bool rollback_transaction(php_mysql_object *obj) {
	if (!obj -> connect -> active_transaction_level)
		return false; 
	bool retval; 
	if (obj -> connect -> active_transaction_level > 1) {
		char *release_query = NULL; 
		int release_query_len = spprintf(
			&release_query, 0, "ROLLBACK TO SAVEPOINT EVILTRANSLEVEL%d", 
			obj -> connect -> active_transaction_level - 1
		);
		
		for (int i = 0; i < 16; ++i) {
			retval = do_evilworlds_mysql_query(obj, release_query, release_query_len, NULL); 
			if (retval)
				break; 
		}
		
		if (release_query)
			efree(release_query); 
	} else {
		for (int i = 0; i < 16; ++i) {
			retval = do_evilworlds_mysql_query(obj, const_str_length("ROLLBACK"), NULL); 
			if (retval)
				break; 
		}
	}
	if (obj -> connect -> active_transaction_level > 0)
		--obj -> connect -> active_transaction_level; 
	return retval; 
}

int evilwordls_mysql_fetch_row(MYSQL_RES *mysql_result, zval *return_value, int result_type) {
	int i = 0; 
	MYSQL_FIELD *mysql_field; 
	MYSQL_ROW mysql_row = mysql_fetch_row(mysql_result); 
	unsigned long *mysql_row_lengths = mysql_fetch_lengths(mysql_result); 
	
	if (!mysql_row || !mysql_row_lengths)
		return 0; 
	
	mysql_field_seek(mysql_result, 0); 
	for (mysql_field = mysql_fetch_field(mysql_result), i = 0; mysql_field; mysql_field = mysql_fetch_field(mysql_result), i++) {
		if (mysql_row[i]) {
			zval *data; 
			
			MAKE_STD_ZVAL(data); 
			ZVAL_STRINGL(data, mysql_row[i], mysql_row_lengths[i], 1); 
			
			if (result_type & MYSQL_NUM)
				add_index_zval(return_value, i, data); 
			
			if (result_type & MYSQL_ASSOC) {
				if (result_type & MYSQL_NUM)
					Z_ADDREF_P(data); 
				add_assoc_zval(return_value, mysql_field -> name, data); 
			}
		} else {
			if (result_type & MYSQL_NUM)
				add_index_null(return_value, i); 
			if (result_type & MYSQL_ASSOC)
				add_assoc_null(return_value, mysql_field -> name); 
		}
	}
	
	return i; 
}

char *prepare_sql_query(char *query, int query_length, int *length, zval ***args, int argc, zval *object) {
	char *ret = estrndup(query, query_length); 
	int arg_n = 0, delta, rep_len; 
	
	*length = query_length; 
	for (int i = 0; arg_n < argc && i < *length; ++i) {
		if (ret[i] == '%' && i + 1 < *length) {
			char *rep = do_prepare_value(*args[arg_n], ret[i + 1], &rep_len, object); 
			if (rep) {
				delta = replace_expression(&ret, *length, i, 2, rep, rep_len); 
				i += delta + 2; *length += delta; 
				efree(rep); 
				++arg_n; 
			} else if (Z_TYPE_PP(args[arg_n]) == IS_ARRAY && i + 2 < *length) {
				if (ret[i + 1] == 'a') {
					rep = do_array_prepare(
						Z_ARRVAL_PP(args[arg_n]), &rep_len, 
						const_str_length(", "), ret[i + 2], true, object
					); 
					if (rep) {
						delta = replace_expression(&ret, *length, i, 3, rep, rep_len); 
						i += delta + 3; *length += delta; 
						efree(rep); 
						++arg_n; 
					}
				} else if (ret[i + 1] == 'h') {
					int offset = 2, separator_len; 
					char separator[6]; 
					
					if (i + 4 < *length && ret[i + 3] == 'O' && ret[i + 4] == 'O') {
						memcpy(separator, const_str_length(" OR ")); 
						separator_len = 2; 
						offset += 2; 
					} else if (i + 4 < *length && ret[i + 3] == 'A' && ret[i + 4] == 'N' && ret[i + 4] == 'D') {
						memcpy(separator, const_str_length(" AND ")); 
						separator_len = 3; 
						offset += 3; 
					} else {
						memcpy(separator, const_str_length(", ")); 
						separator_len = 2; 
					}
					
					rep = do_array_prepare(
						Z_ARRVAL_PP(args[arg_n]), &rep_len, 
						separator, separator_len, ret[i + 2], true, object
					); 
					if (rep) {
						delta = replace_expression(&ret, *length, i, offset, rep, rep_len); 
						i += delta + offset; *length += delta; 
						efree(rep); 
						++arg_n; 
					}
				}
			}
		}
	}
	
	return ret; 
}

char *do_array_prepare(HashTable *data, int *length, const char *sep, int sep_len, char type, bool only_values, zval *self) {
	HashPosition pos; 	
	zval  **value; 
	char  *string_key; 
	uint   string_key_len; 
	ulong  num_key; 
	
	string query = ""; 
	int i = 0, total = zend_hash_num_elements(data); 
	zend_hash_internal_pointer_reset_ex(data, &pos); 
	char *prepared; 
	int prepared_len; 
	
	while (zend_hash_get_current_data_ex(data, (void **)&value, &pos) == SUCCESS) {
		int key_type = zend_hash_get_current_key_ex(data, &string_key, &string_key_len, &num_key, 1, &pos); 
		if (!only_values && key_type == HASH_KEY_IS_STRING)
			query.append("`").append(string_key).append("`"); 
		
		if (Z_TYPE_PP(value) == IS_ARRAY) {
			if (!only_values && key_type == HASH_KEY_IS_STRING)
				query.append(" IN"); 
			if (!only_values)
				query.append(" ("); 
			prepared = do_array_prepare(Z_ARRVAL_PP(value), &prepared_len, ", ", 2, type, only_values, self); 
			query.append(prepared, prepared_len); 
			if (!only_values)
				query.append(")"); 
		} else {
			if (!only_values && key_type == HASH_KEY_IS_STRING)
				query.append(" = "); 
			prepared = do_prepare_value(*value, type, &prepared_len, self); 
			if (prepared) {
				query.append(prepared, prepared_len); 
				efree(prepared); 
			}
		}
		if (++i != total)
			query.append(sep, sep_len); 
		zend_hash_move_forward_ex(Z_ARRVAL_PP(value), &pos); 
	}
	
	*length = query.length(); 
	
	return estrndup(query.c_str(), *length); 
}

char *do_prepare_value(zval *value, char type, int *ret_len, zval *self) {
	char *ret = NULL; 
switch_data_type:
	switch (type) {
		case 'd': case 'u': case 'f': case 'b':
			if ((type == 'd' || type == 'u' || type == 'b') && Z_TYPE_P(value) != IS_LONG)
				convert_to_long_base(value, 10); 
			else if (type == 'f' && Z_TYPE_P(value) != IS_LONG)
				convert_to_double(value); 
			
			if (type == 'u')
				Z_LVAL_P(value) = abs(Z_LVAL_P(value)); 
			
			if (type == 'b')
				Z_LVAL_P(value) = Z_LVAL_P(value) ? 1 : 0; 
			
			convert_to_string(value); 
			*ret_len = Z_STRLEN_P(value); 
			ret = estrndup(Z_STRVAL_P(value), *ret_len); 
		break; 
		
		case 's':
		{
			if (Z_TYPE_P(value) != IS_STRING)
				convert_to_string(value); 
			php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(self TSRMLS_CC); 
			ret = (char *)ecalloc(Z_STRLEN_P(value) + 3, 2); 
			*ret_len = evilworlds_mysql_escape_string(obj -> connect, ret + 1, Z_STRVAL_P(value), Z_STRLEN_P(value)) + 2; 
			ret = (char *)erealloc(ret, *ret_len + 1); 
			ret[0] = '\''; 
			ret[*ret_len - 1] = '\''; 
		}
		break; 
		
		case 'q':
		case 't':
			if (Z_TYPE_P(value) != IS_STRING)
				convert_to_string(value); 
			if (type == 'q') {
				*ret_len = Z_STRLEN_P(value); 
				ret = estrndup(Z_STRVAL_P(value), *ret_len); 
			} else if (type == 't') {
				*ret_len = Z_STRLEN_P(value) + 2; 
				ret = (char *)emalloc(*ret_len + 1); 
				ret[0] = '`'; 
				ret[*ret_len - 1] = '`'; 
				memcpy(ret + 1, Z_STRVAL_P(value), Z_STRLEN_P(value)); 
			}
		break; 
		
		case '?':
		{
			switch (Z_TYPE_P(value)) {
				case IS_STRING: 
					type = 's'; 
					if (IS_START_OF_DIGIT(Z_STRVAL_P(value), Z_STRLEN_P(value))) {
						zend_uchar num_type = is_numeric_string(Z_STRVAL_P(value), Z_STRLEN_P(value), NULL, NULL, 0); 
						if (num_type == IS_LONG)
							type = 'f'; 
						else if (num_type == IS_DOUBLE)
							type = 'l'; 
					}
					goto switch_data_type; 
				
				case IS_LONG: 
					type = 'l'; 
					goto switch_data_type; 
				
				case IS_DOUBLE: 
					type = 'f'; 
					goto switch_data_type; 
				
				case IS_BOOL: 
					type = 'b'; 
					goto switch_data_type; 
				
				case IS_NULL:
					ret = estrndup("NULL", 4); 
					*ret_len = 4; 
				break; 
				
				case IS_ARRAY:
					ret = do_array_prepare(
						Z_ARRVAL_P(value), ret_len, 
						const_str_length(", "), '?', true, self
					); 
				break; 
				
				default: 
					EM_DEBUG("Unkonwn type %d", Z_TYPE_P(value)); 
				break; 
			}
		}	
		break; 
	}
	return ret; 
}

int replace_expression (
	char **text_ptr, int text_length, int start, int len, 
	const char *text0, unsigned short text0_len, 
	const char *text1, unsigned short text1_len, 
	const char *text2, unsigned short text2_len, 
	const char *text3, unsigned short text3_len, 
	const char *text4, unsigned short text4_len, 
	const char *text5, unsigned short text5_len
) {
	int replace_length = text0_len + text1_len + text2_len + text3_len + text4_len + text5_len; 
	int delta = replace_length - len; 
	int end = start + len; 
	
	if (delta < 0)
		memmove(*text_ptr + (end + delta), *text_ptr + end, text_length - end); 
	
	if (delta > 0 || delta < 0)
		*text_ptr = (char *)erealloc(*text_ptr, text_length + delta + 1); 
	*(*text_ptr + text_length + delta) = 0; 
	
	if (delta > 0)
		memmove(*text_ptr + (start + replace_length), *text_ptr + end, text_length - end); 
	
	unsigned int _offset = start; 
	REPLACE_CONCAT_BLOCK(text0); 
	REPLACE_CONCAT_BLOCK(text1); 
	REPLACE_CONCAT_BLOCK(text2); 
	REPLACE_CONCAT_BLOCK(text3); 
	REPLACE_CONCAT_BLOCK(text4); 
	REPLACE_CONCAT_BLOCK(text5); 
	
	return delta; 
}


/*	+-------------------------------+
	| Реализации PHP методов класса |
	+-------------------------------+ */
PHP_METHOD(Mysql, __construct) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	obj -> connect = NULL; 
	obj -> last_result = 0; 
	obj -> logging = false; 
	obj -> log_queries = NULL; 
}

// Mysql::connect(host, user, password, db, port, [persistent = false, flags])
PHP_METHOD(Mysql, connect) {
	zend_bool return_state = true; 
	zend_bool persistent = false; 
	char *addr, *user, *password, *db, *connect_id = NULL, *socket = NULL, *host = NULL; 
	int addr_length, user_length, password_length, db_length; 
	int socket_length = 0, host_length = 0; 
	long port = MYSQL_PORT; 
	long flags = 0; 
	
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssss|lbl", &addr, &addr_length, &user, &user_length, &password, &password_length, &db, &db_length, &port, &persistent, &flags) != SUCCESS)
		WRONG_PARAM_COUNT; 
	obj -> last_result = 0; 
	
	// Закрываем пред. соединение, если есть и не постоянное
	if (obj -> connect && !obj -> connect -> is_persistent)
		evilworlds_mysql_close(obj -> connect); 
	obj -> connect = NULL; 
	
	// Если начинается на : - то это unix-socket
	if (addr_length >= 2 && addr[0] == ':') {
		socket        = addr + 1; 
		socket_length = addr_length - 1; 
		EM_DEBUG("Unix socket[%d]: %s", socket_length, socket); 
	} else {
		host        = addr; 
		host_length = addr_length; 
		EM_DEBUG("Host[%d]: %s", addr_length, addr); 
	}
	
	// Если запрошено постоянное соединение
	if (persistent) {
		// ID постоянного подключения
		spprintf(&connect_id, 0, "%s,%ld,%s,%s,%ld", addr, port, user, password, flags); 
		auto connect_it = EM_SG(persistent_list).find(connect_id); 
		
		EM_DEBUG("Is persistent connection! Trying to search %s in persistent_list...", connect_id); 
		if (connect_it != EM_SG(persistent_list).end()) {
			EM_DEBUG("Finded connection in persistent_list O_O Testing it..."); 
			obj -> connect = connect_it -> second; 
			// Нашли. Тести его пингом
			if (obj -> connect -> mysql && mysql_ping(obj -> connect -> mysql) != 0) {
				EM_DEBUG("Ehhh.. connection lost :( Trying to reconnect..."); 
				// Недоступно? Пробуем переподключиться. 
				if (mysql_real_connect(obj -> connect -> mysql, host, user, password, NULL, port, socket, flags) == NULL) {
					// А не получилось :( 
					EM_DEBUG (
						"Can't reconnect persistent connection! Mysql error[%d]: %s", 
						mysql_errno(obj -> connect -> mysql), mysql_error(obj -> connect -> mysql)
					); 
					obj -> connect = evilworlds_mysql_close(obj -> connect); 
					EM_SG(persistent_list).erase(connect_it); 
					--EM_SG(persistent_cnt); 
					return_state = false; 
				}
			}
		}
		if (!return_state)
			obj -> connect = NULL; 
		else if (obj -> connect) {
			if (obj -> connect -> active_transaction_level > 0) {
				while (obj -> connect -> active_transaction_level > 0)
					rollback_transaction(obj); 
				obj -> connect -> active_transaction_level = 0; 
			}
		}
	}
	
	// Если соединение не постоянное или постоянное, но не нашли в пуле - создаём новое
	if (!obj -> connect && return_state) {
		obj -> connect = (mysql_connect_meta *)malloc(sizeof(mysql_connect_meta)); 
		memset(obj -> connect, 0, sizeof(mysql_connect_meta)); 
		obj -> connect -> mysql = mysql_init(NULL); 
		
		EM_DEBUG("Trying to connect..."); 
		if (mysql_real_connect(obj -> connect -> mysql, host, user, password, NULL, port, socket, flags) != NULL) {
			EM_DEBUG("Success connected!"); 
			obj -> connect -> is_persistent = persistent; 
			obj -> connect -> active_transaction_level = 0; 
			if (persistent) {
				EM_SG(persistent_list)[connect_id] = obj -> connect; 
				++EM_SG(persistent_cnt); 
			}
		} else {
			EM_DEBUG("Can't connect! :("); 
			if (obj -> connect && obj -> connect -> mysql) {
				zend_throw_exception_ex (
					php_mysql_exception_ce, mysql_errno(obj -> connect -> mysql) TSRMLS_CC, 
					(char *)"%s", mysql_error(obj -> connect -> mysql)
				); 
			} else {
				zend_throw_exception_ex (php_mysql_exception_ce, 0 TSRMLS_CC, (char *)"Can't connect to mysql server!"); 
			}
			return_state = false; 
		}
	}
	
	if (return_state)
		mysql_select_db(obj -> connect -> mysql, db); 
	
	if (!return_state && obj -> connect) {
		free_mysql_connect_meta(obj -> connect); 
		obj -> connect = NULL; 
	}
	
	if (connect_id) {
		efree(connect_id); 
		connect_id = NULL; 
	}
	
	RETURN_BOOL(return_state); 
}

PHP_METHOD(Mysql, setCharset) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	
	char *charset; 
	int charset_length; 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &charset, &charset_length) != SUCCESS)
		WRONG_PARAM_COUNT; 
	
	mysql_set_character_set(obj -> connect -> mysql, charset); 
	RETURN_ZVAL(object, 1, 0); 
}

PHP_METHOD(Mysql, escape) {
	zval *object = getThis(); 
	char *str, *new_str; 
	int str_length, new_str_length; 
	
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_length) != SUCCESS)
		WRONG_PARAM_COUNT; 
	
	new_str = (char *)safe_emalloc(str_length, 2, 1); 
	new_str_length = evilworlds_mysql_escape_string(obj -> connect, new_str, str, str_length); 
	new_str = (char *)erealloc(new_str, new_str_length + 1); 
	
	RETURN_STRINGL(new_str, new_str_length, 0); 
}

PHP_METHOD(Mysql, implode) {
	zval *object = getThis(); 
	HashTable *data; 
	
	char *sep = (char *)", "; 
	int   sep_len = 2; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "H|s", &data, &sep, &sep_len) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	int ret_len; 
	char *ret = do_array_prepare(data, &ret_len, sep, sep_len, '?', false, object); 
	
	RETURN_STRINGL(ret, ret_len, 0); 
}

PHP_METHOD(Mysql, prepare) {
	zval *object = getThis(); 
	
	char *query; 
	zval **entry; 
	HashTable *args; 
	HashPosition pos; 
	long offset = 0; 
	int query_length, i = 0; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sh|l", &query, &query_length, &args, &offset) == FAILURE)
		WRONG_PARAM_COUNT; 
	if (ZEND_NUM_ARGS() == 1)
		RETURN_STRINGL(query, query_length, 1); 
	
	uint total = zend_hash_num_elements(args); 
	zval ***args_array = (zval ***)emalloc(sizeof(zval **) * total); 
	
	zend_hash_internal_pointer_reset_ex(args, &pos); 
	if (offset > 0 && offset < total) {
		for (long i = 0; i < offset; ++i, --total)
			zend_hash_move_forward_ex(args, &pos); 
	}
	
	while (zend_hash_get_current_data_ex(args, (void **)&entry, &pos) == SUCCESS) {
		args_array[i++] = entry; 
		zend_hash_move_forward_ex(args, &pos); 
	}
	
	int length; 
	char *ret = prepare_sql_query(query, query_length, &length, args_array, total, object); 
	efree(args_array); 
	
	RETURN_STRINGL(ret, length, 0); 
}

PHP_METHOD(Mysql, query) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	
	zval ***args; 
	char *query, *real_query; 
	int argc, query_length, real_query_length; 
	
	obj -> last_result = 0; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|+", &query, &query_length, &args, &argc) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	if (ZEND_NUM_ARGS() > 1) {
		real_query = prepare_sql_query(query, query_length, &real_query_length, args, argc, object); 
	} else {
		real_query = query; 
		real_query_length = query_length; 
	}
	
	MYSQL_RES *result; 
	if (!do_evilworlds_mysql_query(obj, real_query, real_query_length, &result)) {
		ZVAL_BOOL(return_value, 0); 
	} else {
		if (result) {
			ZEND_REGISTER_RESOURCE(return_value, result, le_mysql_result); 
			obj -> last_result = Z_LVAL_P(return_value); 
			zval_add_ref(&return_value); 
		} else {
			ZVAL_BOOL(return_value, 1); 
		}
	}
	
	if (real_query != query)
		efree(real_query); 
}

PHP_METHOD(Mysql, fetchArray) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	zval *res = NULL; 
	MYSQL_RES *mysql_result = NULL; 
	long result_type = MYSQL_BOTH; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|rl", &res, &result_type) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	FETCH_RESOURCE_LINK(mysql_result, res, true); 
	array_init_size(return_value, mysql_result -> row_count); 
	if (!evilwordls_mysql_fetch_row(mysql_result, return_value, MYSQL_ASSOC) > 0)
		RETURN_NULL(); 
}

PHP_METHOD(Mysql, numRows) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	zval *res = NULL; 
	MYSQL_RES *mysql_result = NULL; 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &res) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	FETCH_RESOURCE_LINK(mysql_result, res, true); 
	RETURN_LONG(mysql_result -> row_count); 
}

PHP_METHOD(Mysql, fetchRow) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	zval *res = NULL; 
	MYSQL_RES *mysql_result = NULL; 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &res) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	FETCH_RESOURCE_LINK(mysql_result, res, true); 
	array_init_size(return_value, mysql_result -> field_count); 
	if (evilwordls_mysql_fetch_row(mysql_result, return_value, MYSQL_NUM) > 0)
		RETURN_NULL(); 
}

PHP_METHOD(Mysql, fetchAssoc) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	zval *res = NULL; 
	MYSQL_RES *mysql_result = NULL; 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &res) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	FETCH_RESOURCE_LINK(mysql_result, res, true); 
	array_init_size(return_value, mysql_result -> field_count); 
	if (!evilwordls_mysql_fetch_row(mysql_result, return_value, MYSQL_ASSOC) > 0)
		RETURN_NULL(); 
}

PHP_METHOD(Mysql, fetchAll) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	uint i = 0; 
	zval *res = NULL; 
	zval *values = NULL; 
	MYSQL_RES *mysql_result; 
	long result_type = MYSQL_ASSOC; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|rl", &res, &result_type) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	FETCH_RESOURCE_LINK(mysql_result, res, true); 
	array_init_size(return_value, mysql_result -> row_count); 
	
	MAKE_STD_ZVAL(values); 
	array_init(values); 
	while (evilwordls_mysql_fetch_row(mysql_result, values, result_type) > 0) {
		add_index_zval(return_value, i, values); 
		values = NULL; 
		
		if (i != mysql_result -> row_count - 1) {
			MAKE_STD_ZVAL(values); 
			array_init(values); 
		}
		++i; 
	}
}

PHP_METHOD(Mysql, affectedRows) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	RETURN_LONG(mysql_affected_rows(obj -> connect -> mysql)); 
}

PHP_METHOD(Mysql, getInfo) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	array_init(return_value); 
	
	int matched, changed, warnings; 
	const char *info = mysql_info(obj -> connect -> mysql); 
	if (!info || sscanf(info, "Rows matched: %d Changed: %d Warnings: %d", &matched, &changed, &warnings) != 3)
		matched = changed = warnings = 0; 
	
	add_assoc_long_ex(return_value, const_obj_size("matched"), matched); 
	add_assoc_long_ex(return_value, const_obj_size("changed"), changed); 
	add_assoc_long_ex(return_value, const_obj_size("warnings"), warnings); 
}

PHP_METHOD(Mysql, info) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	const char *info = mysql_info(obj -> connect -> mysql); 
	if (info)
		RETURN_STRING(info, 1); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, result) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	zval *res = NULL; 
	MYSQL_ROW mysql_row; 
	MYSQL_RES *mysql_result; 
	long row_offset = 0; 
	unsigned long *mysql_row_lengths; 
	long field_offset = 0; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|rll", &res, &field_offset, &row_offset) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	FETCH_RESOURCE_LINK(mysql_result, res, true); 
	
	mysql_data_seek(mysql_result, row_offset); 
	mysql_field_seek(mysql_result, field_offset); 
	
	mysql_row = mysql_fetch_row(mysql_result); 
	mysql_row_lengths = mysql_fetch_lengths(mysql_result); 
	
	if (mysql_row == NULL || mysql_row_lengths == NULL) {
		EXCEPTION_MYSQL_ERROR("Can't fetch row"); 
		RETURN_NULL(); 
	}
	
	RETURN_STRINGL(mysql_row[row_offset], mysql_row_lengths[field_offset], 1); 
}

PHP_METHOD(Mysql, insertId) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	CHECK_MYSQL_CONNECTION(obj); 
	RETURN_LONG(mysql_insert_id(obj -> connect -> mysql)); 
}

PHP_METHOD(Mysql, free) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	zval *res = NULL; 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|r", &res) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	int res_id = obj -> last_result; 
	if (res)
		res_id = Z_LVAL_P(res); 
	if (res_id)
		zend_list_delete(res_id); 
	
	RETURN_ZVAL(object, 1, 0); 
}

PHP_METHOD(Mysql, getInstance) {
	zend_class_entry **ce; 
	zval *instance; 
	
	null_if_fail(zend_lookup_class(const_str_length(MYSQL_CLASS_NAME), &ce TSRMLS_CC) == SUCCESS); 
	instance = zend_read_static_property(*ce, const_str_length("instance"), false TSRMLS_CC); 
	
	if (Z_TYPE_P(instance) != IS_OBJECT) {
		FREE_ZVAL(instance); 
		ALLOC_ZVAL(instance); 
		object_init_ex(instance, *ce); 
		
		if (!call_method(instance, const_str_length(ZEND_CONSTRUCTOR_FUNC_NAME), NULL, 0)) {
			FREE_ZVAL(instance); 
			RETURN_NULL(); 
		}
		
		zend_update_static_property(*ce, const_str_length("instance"), instance TSRMLS_CC); 
	}
	
	Z_SET_REFCOUNT_P(instance, 1); 
	Z_SET_ISREF_P(instance); 
	
	RETURN_ZVAL(instance, 1, 0); 
}

PHP_METHOD(Mysql, setLoggingQueries) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &obj -> logging) == FAILURE)
		WRONG_PARAM_COUNT; 
	if (obj -> logging && !obj -> log_queries) {
		MAKE_STD_ZVAL(obj -> log_queries); 
		array_init(obj -> log_queries); 
	}
	RETURN_ZVAL(object, 1, 0); 
}

PHP_METHOD(Mysql, getQueriesList) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (!obj -> log_queries) {
		array_init(return_value); 
		return; 
	}
	RETURN_ZVAL(obj -> log_queries, 1, 0); 
}


PHP_METHOD(Mysql, resetQueriesList) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (obj -> log_queries) {
		FREE_ZVAL(obj -> log_queries); 
		MAKE_STD_ZVAL(obj -> log_queries); 
		array_init(obj -> log_queries); 
	}
	RETURN_ZVAL(object, 1, 0); 
}

PHP_METHOD(Mysql, ping) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	CHECK_MYSQL_CONNECTION(obj); 
	RETURN_BOOL(mysql_ping(obj -> connect -> mysql) == 0); 
}

PHP_METHOD(Mysql, close) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (obj -> connect && obj -> connect -> is_persistent)
		obj -> connect = NULL; 
	else
		obj -> connect = evilworlds_mysql_close(obj -> connect); 
	obj -> last_result = 0; 
	RETURN_ZVAL(object, 1, 0); 
}

PHP_METHOD(Mysql, error) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (obj -> connect && obj -> connect -> mysql)
		RETURN_STRING(mysql_error(obj -> connect -> mysql), 1); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, errno) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	if (obj -> connect && obj -> connect -> mysql)
		RETURN_LONG(mysql_errno(obj -> connect -> mysql)); 
	RETURN_LONG(0); 
}

// Строит параметры для коллбэка транзакций
zval **trans_build_call_args(zval **params, uint params_count, uint *new_params_count, zval *object, zval *args) {
	uint total = args ? zend_hash_num_elements(Z_ARRVAL_P(args)) : 0; 
	if (!params) {
		params = (zval **)ecalloc(total + 1, sizeof(zval *)); 
	} else if (total + 1 != params_count) {
		params = (zval **)erealloc(params, (total + 1) * sizeof(zval *)); 
	}
	
	// Ссылка на текущий инстанс Evilworlds\Mysql
	MAKE_STD_ZVAL(params[0]); 
	ZVAL_ZVAL(params[0], object, 1, 0); 
	
	// Если переданы юзерские параметры
	if (total > 0) {
		HashPosition pos; 
		zval **entry; 
		
		// Подгатавливаем юзерские данные для функции
		unsigned int i = 1; 
		zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(args), &pos); 
		while (zend_hash_get_current_data_ex(Z_ARRVAL_P(args), (void **)&entry, &pos) == SUCCESS) {
			// Клонируем zval
			MAKE_STD_ZVAL(params[i]); 
			ZVAL_ZVAL(params[i], *entry, 1, 0); 
			Z_SET_REFCOUNT_P(params[i], 1); 
			zend_hash_move_forward_ex(Z_ARRVAL_P(args), &pos); 
			++i; 
		}
	}
	
	*new_params_count = total + 1; 
	return params; 
}


PHP_METHOD(Mysql, beginTransaction) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	long flags = 0; 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &flags) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	CHECK_MYSQL_CONNECTION(obj); 
	ZVAL_BOOL(return_value, start_transaction(obj, flags)); 
}

PHP_METHOD(Mysql, commit) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	ZVAL_BOOL(return_value, commit_transaction(obj)); 
}

PHP_METHOD(Mysql, rollback) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	ZVAL_BOOL(return_value, rollback_transaction(obj)); 
}

PHP_METHOD(Mysql, transaction) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	
	zval *callback, *on_success = NULL, *on_error = NULL, *retval = NULL, *args = NULL; 
	long flags = 0; 
	
	bool in_trans = obj -> connect -> active_transaction_level > 0; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z!z!la!", &callback, &on_success, &on_error, &flags, &args) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	// Проверяем callback-и
	if (!zend_is_callable(callback, 0, NULL TSRMLS_CC)) {
		zend_throw_exception_ex (
			php_mysql_exception_ce, 0 TSRMLS_CC, 
			(char *)"Invalid callback for transaction!"
		); 
		RETURN_FALSE; 
	}
	
	if (on_success && !zend_is_callable(on_success, 0, NULL TSRMLS_CC)) {
		zend_throw_exception_ex (
			php_mysql_exception_ce, 0 TSRMLS_CC, 
			(char *)"Invalid callback for transaction onSuccess!"
		); 
		RETURN_FALSE; 
	}
	
	if (on_error && !zend_is_callable(on_error, 0, NULL TSRMLS_CC)) {
		zend_throw_exception_ex (
			php_mysql_exception_ce, 0 TSRMLS_CC, 
			(char *)"Invalid callback for transaction onError!"
		); 
		RETURN_FALSE; 
	}
	
	zval **params = NULL; uint count_params = 0; 
	RETVAL_BOOL(0); 
	MAKE_STD_ZVAL(retval); 
	
	// Начинаем транзакцию
	bool is_current_trans_opened = false; 
	if (((flags & TRANS_SINGLE) && in_trans) || start_transaction(obj, flags)) {
		is_current_trans_opened = true; 
	//	zend_try {
			params = trans_build_call_args(NULL, 0, &count_params, object, args); 
			// Вызываем функцию транзакции
			if (call_user_function(CG(function_table), NULL, callback, retval, count_params, params TSRMLS_CC) == SUCCESS) {
				if (retval) {
					if (Z_TYPE_P(retval) != IS_BOOL)
						convert_to_boolean(retval); 
					if (Z_BVAL_P(retval) && !EG(exception) && (((flags & TRANS_SINGLE) && in_trans) || commit_transaction(obj))) {
						if (on_success) {
							params = trans_build_call_args(params, 0, &count_params, object, args); 
							call_user_function(CG(function_table), NULL, on_success, retval, count_params, params TSRMLS_CC); 
						}
						RETVAL_BOOL(1); // В zend_try нельзя делать return, эх...
					}
				}
			}
	//	} zend_catch {
			// php internal error... 
	//	} zend_end_try(); 
	}
	
	if (!Z_BVAL_P(return_value)) {
		if (!(in_trans && (flags & TRANS_SINGLE)) && is_current_trans_opened)
			rollback_transaction(obj); 
		zend_exception_save(); // Сохраним текущее исключение
		if (on_error) {
			params = trans_build_call_args(params, 0, &count_params, object, args); 
			call_user_function(CG(function_table), NULL, on_error, retval, count_params, params TSRMLS_CC); 
		}
		zend_exception_restore(); // Восстановим предыдущее исключение
	}
	
	if (params) {
		efree(params); 
		params = NULL; 
	}
}

PHP_METHOD(Mysql, inTransaction) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	CHECK_MYSQL_CONNECTION(obj); 
	RETURN_BOOL(obj -> connect -> active_transaction_level > 0); 
}

PHP_METHOD(Mysql, getMacrosByType) {
	long attr; 
	char *type; int type_length; 
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &type, &type_length, &attr) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	char macros = '?';
	for (unsigned char i = 0; i < ARRAY_SIZE(mysql_types_to_macros); ++i)
		if (strncmp(type, mysql_types_to_macros[i].mysql_type, type_length) == 0)
			macros = mysql_types_to_macros[i].macros_name;
	RETURN_STRINGL(&macros, 1, 1); 
}

PHP_METHOD(Mysql, getTableFields) {
	zval *object = getThis(); 
	php_mysql_object *obj = (php_mysql_object *)zend_object_store_get_object(object TSRMLS_CC); 
	
	CHECK_MYSQL_CONNECTION(obj); 
	
	int i = 0, table_name_length = 0, query_length, db_name_length = 0; 
	char *query, *table_name = NULL, *db_name = NULL; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &table_name, &table_name_length, &db_name, &db_name_length) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	if (db_name)
		query_length = spprintf(&query, 0, "SHOW FIELDS FROM `%s`.`%s`", db_name, table_name); 
	else
		query_length = spprintf(&query, 0, "SHOW FIELDS FROM `%s`", table_name); 
	
	MYSQL_RES *result = NULL; 
	bool res = do_evilworlds_mysql_query(obj, query, query_length, &result); 
	efree(query); 
	
	if (!res || !result)
		RETURN_NULL(); 
	
	zval *field = NULL; 
	MYSQL_ROW mysql_row; 
	
	array_init(return_value); 
	
	int m = 0, d = 0; 
	char type[64], attr[64]; 
	unsigned int tmp_attr; 
	unsigned long *lengths; 
	
	memset(attr, 0, sizeof(attr)); 
	memset(type, 0, sizeof(type)); 
	
	while ((mysql_row = mysql_fetch_row(result)) != NULL) {
		lengths = mysql_fetch_lengths(result); 
		
		if (!field) {
			MAKE_STD_ZVAL(field); 
			array_init(field); 
		}
		
		if (sscanf(mysql_row[SHOW_FIELDS_TYPE], "%[^( ](%d,%d) %[^\n]", (char *)&type, &m, &d, (char *)&attr) > 2) {
			// nothing todo
		} else if (sscanf(mysql_row[SHOW_FIELDS_TYPE], "%[^( ](%d) %[^\n]", (char *)&type, &m, (char *)&attr) > 1) {
			d = 0; 
		} else if (sscanf(mysql_row[SHOW_FIELDS_TYPE], "%[^( ] %[^\n]", (char *)&type, (char *)&attr) > 0) {
			m = d = 0; 
		}
		
		tmp_attr = 0; 
		if (strstr(attr, "unsigned"))
			tmp_attr |= FIELD_ATTR_UNSIGNED; 
		if (strstr(attr, "zerofill"))
			tmp_attr |= FIELD_ATTR_ZEROFILL; 
		
		add_assoc_long_ex(field, const_obj_size("m"), m); 
		add_assoc_long_ex(field, const_obj_size("d"), d); 
		add_assoc_string_ex(field, const_obj_size("type"), type, 1); 
		add_assoc_long_ex(field, const_obj_size("attr"), tmp_attr); 
		
		if (!mysql_row[SHOW_FIELDS_VALUE])
			add_assoc_stringl_ex(field, const_obj_size("value"), (char *)"", 0, 1); 
		else
			add_assoc_stringl_ex(field, const_obj_size("value"), mysql_row[SHOW_FIELDS_VALUE], lengths[SHOW_FIELDS_VALUE], 1); 
		
		add_assoc_zval_ex(return_value, mysql_row[SHOW_FIELDS_NAME], lengths[SHOW_FIELDS_NAME] + 1, field); 
		field = NULL; 
		
		++i; 
	}
	
	mysql_free_result(result); 
}

PHP_METHOD(Mysql, prepareIntValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, 'd', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, prepareUnsignedIntValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, 'u', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, prepareStringValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, 's', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, prepareBoolValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, 'b', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, prepareFloatValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, 'f', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, prepareQueryValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, 'q', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, prepareTableNameValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, 't', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}

PHP_METHOD(Mysql, prepareValue) {
	zval *value, *object = getThis(); 
	int return_length; 
	char *ret_value; 
	
	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE)
		WRONG_PARAM_COUNT; 
	
	ret_value = do_prepare_value(value, '?', &return_length, object); 
	if (ret_value)
		RETURN_STRINGL(ret_value, return_length, 0); 
	RETURN_NULL(); 
}
